package cn.org.itool.ws.framework.ws.command.impl;

import cn.org.itool.ws.framework.annotation.CommandMapping;
import cn.org.itool.ws.framework.annotation.Handler;
import cn.org.itool.ws.framework.ws.command.CommandHandlerMethodRegistry;
import cn.org.itool.ws.framework.ws.command.po.HandlerMethod;
import io.netty.channel.Channel;
import jakarta.annotation.PostConstruct;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;


@Log4j2
public class DefaultCommandHandlerMethodRegistry implements CommandHandlerMethodRegistry,ApplicationContextAware {

    private ApplicationContext applicationContext;

    private final Map<String, HandlerMethod> handlerMethodMap = new ConcurrentHashMap<>(64);

    @PostConstruct
    public void initHandler(){
        String[] beanNames = applicationContext.getBeanNamesForType(Object.class);
        for (String beanName : beanNames) {
            registerHandlerBean(beanName);
        }
    }

    @Override
    public Parameter[] getMethodArg(String command) {
        if (handlerMethodMap.containsKey(command)) {
            return handlerMethodMap.get(command).getMethodArg();
        } else {
            return null;
        }

    }

    @Override
    public Method getMethod(String command) {
        if (handlerMethodMap.containsKey(command)) {
            return handlerMethodMap.get(command).getMethod();
        } else {
            return null;
        }
    }


    @Override
    public Object getServiceInstance(String command) {
        if (handlerMethodMap.containsKey(command)) {
            HandlerMethod handlerMethod = handlerMethodMap.get(command);
            return handlerMethod.createWithResolvedBean().getServiceInstance();
        } else {
            return null;
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private void registerHandlerBean(String beanName) {
        Class<?> beanType = null;
        try {
            beanType = applicationContext.getType(beanName);
        } catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            log.error("Could not resolve type for bean '" + beanName + "'", ex);
        }

        if (Objects.nonNull(beanType) && AnnotatedElementUtils.hasAnnotation(beanType, Handler.class)){

            ReflectionUtils.doWithMethods(beanType, method -> {
                if (AnnotatedElementUtils.hasAnnotation(method, CommandMapping.class)) {
                    CommandMapping annotation = method.getAnnotation(CommandMapping.class);
                    String[] commandArr = annotation.value();
                    for (String command : commandArr) {
                        Parameter[] parameters = method.getParameters();
                        if (parameters.length == 0) {
                            HandlerMethod handlerMethod = new HandlerMethod(applicationContext, beanName, method, null);
                            handlerMethodMap.put(command, handlerMethod);
                        } else if (parameters.length == 1) {
                            HandlerMethod handlerMethod = new HandlerMethod(applicationContext, beanName, method, parameters);
                            handlerMethodMap.put(command, handlerMethod);
                        } else {
                            int i = parameters.length;
                            for (Parameter parameter : parameters) {
                                // 检查参数是否与当前对象属于同一类
                                if (parameter.getType() == Channel.class) {
                                    i --;
                                }
                            }
                            HandlerMethod handlerMethod = new HandlerMethod(applicationContext, beanName, method, parameters);
                            if(Objects.nonNull(handlerMethodMap.put(command, handlerMethod))) {
                                throw new IllegalArgumentException(String.format("command 重复 %s", command));
                            }
                            if(i > 1) {
                                throw new IllegalArgumentException(String.format("%s handler method %s 不能多于一个参数", beanName, method.getName()));
                            }

                        }
                    }
                }
            }, ReflectionUtils.USER_DECLARED_METHODS);
        }
    }
}
